﻿/*
 * @author: wizardc
 */

using Dou.Job;
using Unity.Burst;
using Unity.Collections;
using UnityEngine;

namespace Demo.WorldMap.GPU
{
    /// <summary>
    /// 对删除数据进行排序，保证删除后数据的正确性
    /// 如果出现删除buff数据的情况，需要修正所有后续可视节点的指向内存的数据索引为正确值
    /// </summary>
    [BurstCompile]
    public unsafe class RemoveDataSortJob : ManagedJobParallelFor<RemoveDataSortJob>.IWork
    {
        public GPUDrawContainer _container;
        
        public GPUStaticRenderNode* nodePtr;
        public GPUStaticRenderLeaf* leafPtr;
        
        public NativeList<int> visibleNodeList;
        
        [BurstCompile]
        [Il2CppSetOption(Option.ArrayBoundsChecks, false)]
        [Il2CppSetOption(Option.NullChecks, false)]
        public void Execute(int index)
        {
            var resInfo = _container.GetResInfo(index);
            var removeRange = resInfo.removeRange;
            if (removeRange.Length > 1)
            {
                // 数据移除时必须是从后向前来删除，否则如果先删除了前面的数据，再删除后面的数据时会出现异常
                // 这里按照从大到小的顺序进行排序，后续使用时直接按顺序获取即可
                Sort(ref removeRange);
            }
            // 修正每个叶子节点中的buffer索引，重置 newAdd 标识
            ReviseBufferIndex(index, ref removeRange);
        }

        [BurstCompile]
        [Il2CppSetOption(Option.ArrayBoundsChecks, false)]
        [Il2CppSetOption(Option.NullChecks, false)]
        private void Sort(ref NativeList<Vector2Int> removeRange)
        {
            // 快速排序
            QuickSort(ref removeRange, 0, removeRange.Length - 1);
        }
        
        [BurstCompile]
        [Il2CppSetOption(Option.ArrayBoundsChecks, false)]
        [Il2CppSetOption(Option.NullChecks, false)]
        private static void QuickSort(ref NativeList<Vector2Int> array, int low, int high)
        {
            if (low < high)
            {
                var p = Partition(ref array, low, high);
                QuickSort(ref array, low, p - 1);
                QuickSort(ref array, p + 1, high);
            }
        }
        
        [BurstCompile]
        [Il2CppSetOption(Option.ArrayBoundsChecks, false)]
        [Il2CppSetOption(Option.NullChecks, false)]
        private static int Partition(ref NativeList<Vector2Int> array, int low, int high)
        {
            var pivot = array[high].x;
            var i = low - 1;
            for (var j = low; j < high; j++)
            {
                if (array[j].x > pivot)
                {
                    i++;
                    var t = array[i];
                    array[i] = array[j];
                    array[j] = t;
                }
            }
            var temp = array[i + 1];
            array[i + 1] = array[high];
            array[high] = temp;
            return i + 1;
        }

        // TODO : 这里的做法是修正所有叶节点的 buffer 索引，另外一种做法是直接重新对 buffer 进行排序后直接赋值新的 buffer 索引
        [BurstCompile]
        [Il2CppSetOption(Option.ArrayBoundsChecks, false)]
        [Il2CppSetOption(Option.NullChecks, false)]
        private void ReviseBufferIndex(int index, ref NativeList<Vector2Int> removeRange)
        {
            // 仅处理仍然显示的节点，注意移除的节点已经不在这个列表中了
            for (int i = 0; i < visibleNodeList.Length; i++)
            {
                var nodeIndex = visibleNodeList[i];
                
                ref var node = ref nodePtr[nodeIndex];
                
                var startLeafIndex = node.startLeafIndex;
                var leafCount = node.leafCount;
                
                for (int j = 0; j < leafCount; j++)
                {
                    var leafIndex = startLeafIndex + j;
                    ref var leaf = ref leafPtr[leafIndex];
                    
                    // 是当前处理的 res 类型就处理
                    if (leaf.resId == index)
                    {
                        // 叶节点非新增节点才需要重新计算 buffer 索引
                        if (!leaf.newAdd)
                        {
                            var startBufferIndex = leaf.startBufferIndex;
                            
                            var removeCount = removeRange.Length;
                            for (int k = removeCount - 1; k >= 0; k--)
                            {
                                var remove = removeRange[k];
                                var startIndex = remove.x; // 移除的开始索引
                                var count = remove.y; // 移除的数量
                                
                                // 比要删除的还小就可以不继续判断了
                                if (startBufferIndex < startIndex) // 用来判断的索引必须是没有校正过的索引
                                {
                                    break;
                                }
                                leaf.startBufferIndex -= count;
                                
                                // 调试
                                if (leaf.startBufferIndex < 0)
                                {
                                    Debug.LogError("索引异常");
                                }
                            }
                        }
                        else
                        {
                            // 新增节点设定为非新增
                            leaf.newAdd = false;
                        }
                    }
                }
            }
        }
    }
}
